import {EventDispatcher, Spherical} from 'three';
import {TWEEN} from "three/examples/jsm/libs/tween.module.min.js";
import * as THREE from "three";

var MoveLookAtControls = function(camera, domElement) {
  if (domElement === undefined) {
    console.warn('THREE.MoveLookAtControls: The second parameter "domElement" is now mandatory.');
    domElement = document.body;
  }

  this.customParams = {}

  this.domElement = domElement;

  // Set to constrain the pitch of the camera 设置为限制摄像机的俯仰
  // Range is 0 to Math.PI radians 范围是0到Math.PI弧度
  this.minPolarAngle = 0; // radians
  this.maxPolarAngle = Math.PI; // radians

  // internals
  var scope = this;

  var gravity = 9.8
  var cameraH = camera.position.y

  var cameraP = camera.position
  var mouseSlide = false
  var moveX = 0, // 把鼠标屏幕上的横向偏移量，作为 旋转角度的基准
    moveY = 0, // 把鼠标在屏幕上的纵向偏移量 组委旋转角度的基准
    target = new THREE.Vector3(), // 相机看向的方向
    v = 0.1 // 转动速度
  var startX, startY;

  var spherical = new Spherical();

  var moveForward = false,
    moveBackward = false,
    moveLeft = false,
    moveRight = false,
    canJump = true,
    moveV = 1;

  this.startFun = undefined
  this.moveFun = undefined
  this.endFun = undefined

  function start(e) {
    scope.domElement.setAttribute('tabindex', '0')
    if (scope.startFun) scope.startFun(e)

    startX = e.pageX || e.touches[0].pageX
    startY = e.pageY || e.touches[0].pageY
  }

  function move(e) {
    if (scope.moveFun) scope.moveFun(e)

    if (mouseSlide || e.touches) {

      const pageX = e.pageX || e.touches[0].pageX, pageY = e.pageY || e.touches[0].pageY
      moveX = moveX + (startX - pageX) * v
      moveY = moveY + (pageY - startY) * v
      if (moveY >= THREE.MathUtils.radToDeg(scope.maxPolarAngle - Math.PI / 2) - .001) {
        moveY = THREE.MathUtils.radToDeg(scope.maxPolarAngle - Math.PI / 2) - .001
      } else if (moveY <= THREE.MathUtils.radToDeg(scope.minPolarAngle - Math.PI / 2) + .001) {
        moveY = THREE.MathUtils.radToDeg(scope.minPolarAngle - Math.PI / 2) + .001
      }

      startX = pageX
      startY = pageY
    }
  }

  function end(e) {
    if (scope.endFun) scope.endFun(e)
  }

  {
    scope.domElement.addEventListener('keydown', onKeyDown, false)
    scope.domElement.addEventListener('keyup', onKeyUp, false)

    scope.domElement.addEventListener('touchstart', start, false)
    scope.domElement.addEventListener('touchmove', move, false)
    scope.domElement.addEventListener('touchend', end, false)
    scope.domElement.addEventListener('mousedown', function(ev) {
      start(ev)
      mouseSlide = true
      scope.domElement.addEventListener('mousemove', move, false)
    }, false)
    scope.domElement.addEventListener('mouseup', function(e) {
      mouseSlide = false
      end(e)
    }, false)
    scope.domElement.addEventListener('mouseout', function() {
      mouseSlide = false
    }, false)
  }

  this.dispose = function() {
    scope.domElement.removeEventListener('keydown', onKeyDown)
    scope.domElement.removeEventListener('keyup', onKeyUp)

    scope.domElement.removeEventListener('touchstart', start);
    scope.domElement.removeEventListener('touchmove', move);
    scope.domElement.removeEventListener('touchend', end)

    scope.domElement.removeEventListener('mousedown', start);
    scope.domElement.removeEventListener('mousemove', move);
    scope.domElement.removeEventListener('mouseup', end)
  };

  this.getObject = function() { // retaining this method for backward compatibility 保留此方法以实现向后兼容
    return camera;
  };

  this.getDirection = function() {
    spherical.phi = THREE.MathUtils.degToRad(moveY)
    spherical.theta = THREE.MathUtils.degToRad(moveX)
    return spherical
  };

  function jumpCamera(camera, cameraH, gravity, time, tween) {
    const jumpH = 9.8 * time - gravity * time * time / 2
    if (jumpH <= 0) {
      camera.position.setY(cameraH)
    } else {
      camera.position.setY(cameraH + jumpH)
    }
  }

  function onKeyDown(event) {
    switch (event.code) {
      case 'ArrowUp':
      case 'KeyW':
        moveForward = true;
        break;

      case 'ArrowLeft':
      case 'KeyA':
        moveLeft = true;
        break;

      case 'ArrowDown':
      case 'KeyS':
        moveBackward = true;
        break;

      case 'ArrowRight':
      case 'KeyD':
        moveRight = true;
        break;

      case 'Space':
        if (canJump === true) {
          new TWEEN.Tween({time: 0})
            .to({time: 1000})
            .onUpdate(val => {
              jumpCamera(camera, cameraH, gravity, val.time / 500)
            }).onComplete(val => {
            canJump = true
          }).easing(TWEEN.Easing.Linear.None).start();
        }
        canJump = false;
        break;

      case 'ShiftLeft':
      case 'ShiftRight':
        moveV = 3;
        break;
    }
  };

  function onKeyUp(event) {

    switch (event.code) {

      case 'ArrowUp':
      case 'KeyW':
        moveForward = false;
        break;

      case 'ArrowLeft':
      case 'KeyA':
        moveLeft = false;
        break;

      case 'ArrowDown':
      case 'KeyS':
        moveBackward = false;
        break;

      case 'ArrowRight':
      case 'KeyD':
        moveRight = false;
        break;

      case 'ShiftLeft':
      case 'ShiftRight':
        moveV = 1;
        break;
    }

  };

  this.moveForward = function(distance) {
    camera.position.z -= distance * Math.cos(THREE.MathUtils.degToRad(moveX)) * moveV
    camera.position.x += distance * Math.sin(THREE.MathUtils.degToRad(moveX)) * moveV
  };

  this.moveRight = function(distance) {
    camera.position.x += distance * Math.cos(THREE.MathUtils.degToRad(moveX)) * moveV
    camera.position.z += distance * Math.sin(THREE.MathUtils.degToRad(moveX)) * moveV
  };

  this.getPosition = function() {
    return camera.position
  }

  this.fromMoveTo = function(from, to, time) {
    new TWEEN.Tween(from).to(to, time || 1000).easing(TWEEN.Easing.Sinusoidal.InOut).start();
  }

  this.update = function() {

    target.setX(cameraP.x + Math.cos(THREE.MathUtils.degToRad(moveX - 90)) * Math.cos(THREE.MathUtils.degToRad(moveY)))
    target.setY(cameraP.y + Math.sin(THREE.MathUtils.degToRad(moveY)))
    target.setZ(cameraP.z + Math.sin(THREE.MathUtils.degToRad(moveX - 90)) * Math.cos(THREE.MathUtils.degToRad(moveY)))
    camera.lookAt(target)

    const s = .25
    const forward = moveForward ? s : moveBackward ? -s : 0;
    const left = moveLeft ? -s : moveRight ? s : 0;
    if (forward) scope.moveForward(forward);
    if (left) scope.moveRight(left);

    TWEEN.update()
  }
  setInterval(ev => {
    // console.log(moveX)
  }, 1000)
};

MoveLookAtControls.prototype = Object.create(EventDispatcher.prototype);
MoveLookAtControls.prototype.constructor = MoveLookAtControls;

export {MoveLookAtControls};
